package com.iteaj.iot.client;

import com.iteaj.iot.CoreConst;
import com.iteaj.iot.client.component.TcpClientComponent;
import com.iteaj.iot.codec.SocketMessageDecoder;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;

import java.util.concurrent.TimeUnit;

import static com.iteaj.iot.CoreConst.*;

/**
 * create time: 2021/8/6
 *
 * @author iteaj
 * @since 1.0
 */
public abstract class TcpSocketClient extends SocketClient {

    public TcpSocketClient(TcpClientComponent clientComponent, ClientConnectProperties config) {
        super(clientComponent, config);
    }

    @Override
    protected Class<? extends Channel> channel() {
        return NioSocketChannel.class;
    }

    @Override
    public TcpClientComponent getClientComponent() {
        return (TcpClientComponent) super.getClientComponent();
    }

    /**
     * 新增连接超时校验
     * @param channel
     */
    @Override
    protected void doInitChannel(Channel channel) {
        if(getConfig().getReaderIdleTime() > 0 || getConfig().getReaderIdleTime() > 0
                || getConfig().getAllIdleTime() > 0) {

            // 连接超时处理
            channel.pipeline().addAfter(CLIENT_ENCODER_HANDLER, IDLE_STATE_EVENT_HANDLER, initIdleStateHandler(getConfig()));
        }
    }

    /**
     * 初始化空闲时间监听handler
     * @param properties
     * @return
     */
    protected IdleStateHandler initIdleStateHandler(ClientConnectProperties properties) {
        return new IdleStateHandlerAdapter(properties.getReaderIdleTime()
                , properties.getWriterIdleTime()
                , properties.getAllIdleTime()
                , TimeUnit.SECONDS);
    }

    public class IdleStateHandlerAdapter extends IdleStateHandler {

        public IdleStateHandlerAdapter(long readerIdleTime, long writerIdleTime, long allIdleTime, TimeUnit unit) {
            super(readerIdleTime, writerIdleTime, allIdleTime, unit);
        }

        @Override
        protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {
            ClientConnectProperties config = ctx.channel().attr(CoreConst.CLIENT_KEY).get();
            Object idle = getClientComponent().idle(config.connectKey(), evt.state());

            if(idle instanceof IdleState) { // 返回超时状态直接关闭连接
                long timeout = idle == IdleState.ALL_IDLE ? config.getAllIdleTime() :
                        idle == IdleState.READER_IDLE ? config.getReaderIdleTime() :
                                config.getWriterIdleTime();

                // 声明连接关闭是因为客户端超时导致
                ctx.channel().attr(CoreConst.CLIENT_TIMEOUT_CLOSED).set(timeout);
                ctx.channel().close();
            } else if(idle != null){ // 返回值不为空则直接写出
                writeAndFlush(idle);
            } else {
                // 什么都不做
            }

            ctx.fireUserEventTriggered(evt);
        }
    }

    /**
     * @see SocketMessageDecoder 基于netty的常用tcp解码器适配
     * @return
     */
    @Override
    protected abstract ChannelInboundHandler createProtocolDecoder();
}
